package p02_容器的经典实现类;

import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;


/**
 * 相比于BeanFactory的简陋功能，ApplicationContext则要强大许多
 * ApplicationContext主动做好了很多工作，比如加入并运行后处理器，初始化好所有单例bean...
 * ApplicationContext接口有四个实现类
 */
public class _2_ApplicationContext经典实现 {

    public static void main(String[] args) {
        _2_ApplicationContext经典实现 impl = new _2_ApplicationContext经典实现();
        /**
         * 这两个实现类内部是基于DefaultListableBeanFactory创建的
         * 通过XmlBeanDefinitionReader读取xml并转化为bean定义对象加入容器，再根据DefaultListableBeanFactory创建出bean
         *      XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
         *      reader.loadBeanDefinitions("xml文件对象")
         * 这两个实现类并不会默认添加后处理器，必须在xml文件中手动添加<context:annotation-config/>
         */
        impl.classPathXmlApplicationContext();
        impl.fileSystemXmlApplicationContext();
        /**
         * 该实现类添加了5个后处理器，所以该实现类天然具有解析一些注解的能力
         */
        impl.annotationConfigApplicationContext();
        /**
         * 这种实现类借助了内嵌tomcat容器作为服务器，tomcat本质就是容器内的一个bean
         */
        impl.annotationConfigServletWebServerApplicationContext();
    }

    /**
     * 经典容器：从类路径下读取xml文件作为容器
     */
    public void classPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("classpath:p02/TestPathXml.xml");
        //上面的代码与下面四行代码等价
//        DefaultListableBeanFactory context = new DefaultListableBeanFactory();
//        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
//        reader.loadBeanDefinitions("classpath:p02/TestPathXml.xml");
//        context.preInstantiateSingletons();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    /**
     * 经典容器：从磁盘路径下读取xml文件作为容器，与classPathXmlApplicationContext别无二致
     * 相对路径写法可以从src目录开始
     */
    public void fileSystemXmlApplicationContext() {
        //这里路径按照配置文件实际路径
        FileSystemXmlApplicationContext context =
                new FileSystemXmlApplicationContext("src/main/resources/p02/TestPathXml.xml");
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        context.close();
    }

    /**
     * 经典容器：基于配置类作为容器
     * 该实现类自动添加了5种后处理器
     */
    public void annotationConfigApplicationContext() {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(Config.class);
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        context.close();
    }

    /**
     * 在第三种基础上，增加了有web的能力
     */
    public void annotationConfigServletWebServerApplicationContext() {
        /**
         * web环境，需要更多的组件，所以需要加入更多的bean，比如
         *  1.ServletWebServerFactory(产生tomcat服务器)
         *  2.DispatcherServlet(前处理器)
         *  3.DispatcherServletRegistrationBean(将前处理注册为tomcat服务器的一个servlet)
         *  除了以上三个必备的组件，还可以加入一些诸如Controller控制器bean
         */
        AnnotationConfigServletWebServerApplicationContext context =
                new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    @Configuration
    static class WebConfig {
        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            //创建服务器bean
            return new TomcatServletWebServerFactory();
        }

        @Bean
        public DispatcherServlet dispatcherServlet() {
            //springmvc的前处理器
            return new DispatcherServlet();
        }

        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            //将前处理器注册到服务器
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        //hello作为该控制器的访问路径
        @Bean("/hello")
        public Controller controller1() {
            //加入控制器对象
            return (request, response) -> {
                response.getWriter().println("hello");
                return null;
            };
        }
    }

    static class Bean1 {
        private Bean2 bean2;

        public void setBean(Bean2 bean2) {
            this.bean2 = bean2;
        }
    }

    static class Bean2 {

    }


}
